package com.tonyodev.fetch2;

import ohos.utils.Pair;

import com.tonyodev.fetch2.exception.FetchException;
import com.tonyodev.fetch2.fetch.FetchImpl;
import com.tonyodev.fetch2.fetch.FetchModulesBuilder;
import com.tonyodev.fetch2core.DownloadBlock;
import com.tonyodev.fetch2core.Downloader;
import com.tonyodev.fetch2core.Extras;
import com.tonyodev.fetch2core.FetchErrorStrings;
import com.tonyodev.fetch2core.FetchObserver;
import com.tonyodev.fetch2core.FileResource;
import com.tonyodev.fetch2core.Func;
import com.tonyodev.fetch2core.Func2;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A light weight file download manager for Android.
 *
 * @since 2021-05-20
 */
public interface Fetch {
    /**
     * Returns true if this instance of fetch is closed and cannot be reused.
     *
     * @return isClosed
     */
    boolean isClosed();

    /**
     * The namespace which this instance of fetch operates in. An app can
     * have several instances of Fetch with different namespaces.
     *
     * @return namespace
     */
    String getNamespace();

    /**
     * Get the FetchConfiguration object that created this instance of Fetch.
     * Note: If you have updated settings on this instance of Fetch, this object
     * will not have these updated settings.
     *
     * @return FetchConfiguration
     */
    FetchConfiguration getFetchConfiguration();

    /**
     * Queues a request for downloading. If Fetch fails to enqueue the request,
     * func2 will be called with the error.
     * Errors that may cause Fetch to fail the enqueue are :
     * 1. No storage space on the device.
     * 2. Fetch is already managing the same request. This means that a request with the same url
     * and file name is already managed.
     * 3. Fetch is already managing a request that is downloading to the request file.
     *
     * @param request Download Request
     * @param func Func Callback that the enqueued request will be returned on.
     * Fetch may update a request depending on the initial request's Enqueue Action.
     * Update old request references with this request.
     * @param func2 Func2 Callback that is called when enqueuing a request fails. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch enqueue(Request request, Func<Request> func, Func<Error> func2) throws FetchException;

    /**
     * Queues a list of requests for downloading. If Fetch fails to enqueue a
     * download request because an error occurred, all other request in the list will
     * fail.func2 will be called with the error message.
     * Errors that may cause Fetch to fail the enqueue are :
     * 1. No storage space on the device.
     * 2. Fetch is already managing the same request. This means that a request with the same url
     * and file name is already managed.
     * 3. Fetch is already managing a request that is downloading to the request file.
     *
     * @param requests Request List
     * @param func Func Callback that the enqueued or failed requests will be returned on.
     * This callback returns a list with a pair<Request,Error> for each enqueued request.
     * If the pair's second parameter is Error.NONE. this indicates that the request
     * was enqueued successfully. If the Error is not ERROR.NONE. This indicates
     * that the request was not enqueued for the specified reason.
     * Fetch may update a request depending on the initial request's Enqueue Action.
     * Update old request references with this request.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch enqueue(List<Request> requests, Func<List<Pair<Request, Error>>> func);

    /**
     * Pause a queued or downloading download.
     *
     * @param ids ids of downloads to be paused.
     * @param func Func Callback the paused downloads will be returned on. Note. Only downloads that
     * were paused will be returned in the result list.
     * @param func2 Func2 Callback that is called when attempting to pause downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pause(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Pause a queued or downloading download.
     *
     * @param ids ids of downloads to be paused.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pause(List<Integer> ids);

    /**
     * Pause a queued or downloading download.
     *
     * @param id id of download to be paused.
     * @param func Callback where the paused download will be returned on if successful.
     * @param func2 Callback that is called when attempting to pause downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pause(int id, Func<Download> func, Func<Error> func2);

    /**
     * Pause a queued or downloading download.
     *
     * @param id id of download to be paused.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pause(int id);

    /**
     * Pause all queued or downloading downloads within the specified group.
     *
     * @param id specified group id.
     * @param func callback that returns list of downloads that were paused in the group.
     * @param func2 Callback that is called when attempting to pause downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pauseGroup(int id, Func<List<Download>> func, Func<Error> func2);

    /**
     * Pause all queued or downloading downloads within the specified group.
     *
     * @param id specified group id.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pauseGroup(int id);

    /**
     * Pauses all currently downloading items, and pauses all download processing fetch operations.
     * Use this method when you do not want Fetch to keep processing downloads
     * but do not want to release the instance of Fetch. However, you are still able to query
     * download information.
     *
     * @param func callback returning the success of the freeze.
     * @param func2 callback used to report error.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch freeze(Func<Boolean> func, Func<Error> func2);

    /**
     * Pause all downloads associated by this fetch.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch pauseAll();

    /**
     * Pauses all currently downloading items, and pauses all download processing fetch operations.
     * Use this method when you do not want Fetch to keep processing downloads
     * but do not want to release the instance of Fetch. However, you are still able to query
     * download information.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch freeze();

    /**
     * Resume a download that has been paused.
     *
     * @param ids ids of downloads to be resumed.
     * @param func callback where successfully resumed downloads will be returned.
     * @param func2 Callback that is called when attempting to resume downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resume(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Resume a download that has been paused.
     *
     * @param ids ids of downloads to be resumed.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resume(List<Integer> ids);

    /**
     * Resume a download that has been paused.
     *
     * @param id id of download to be resumed.
     * @param func callback where successfully resumed download will be returned.
     * @param func2 Callback that is called when attempting to resume downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resume(int id, Func<Download> func, Func<Error> func2);

    /**
     * Resume a download that has been paused.
     *
     * @param id id of download to be resumed.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resume(int id);

    /**
     * Resume all paused downloads within the specified group.
     *
     * @param id specified group id.
     * @param func callback where successfully resumed downloads will be returned on.
     * @param func2 Callback that is called when attempting to resume downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resumeGroup(int id, Func<List<Download>> func, Func<Error> func2);

    /**
     * Resume all paused downloads associated the specified group.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resumeAll();

    /**
     * Resume all paused downloads within the specified group.
     *
     * @param id specified group id.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resumeGroup(int id);

    /**
     * Allow fetch to resume operations after freeze has been called.
     *
     * @param func callback returning the success of the freeze.
     * @param func2 callback used to report error.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch unfreeze(Func<Boolean> func, Func<Error> func2);

    /**
     * Allow fetch to resume operations after freeze has been called.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch unfreeze();

    /**
     * Remove a list of downloads managed by this instance of Fetch.
     * The downloaded file for the removed downloads are not deleted.
     *
     * @param ids ids of downloads to be removed.
     * @param func callback used to report successfully removed downloads.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch remove(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Remove a list of downloads managed by this instance of Fetch.
     * The downloaded file for the removed downloads are not deleted.
     *
     * @param ids ids of downloads to be removed.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch remove(List<Integer> ids);

    /**
     * Remove a download managed by this instance of Fetch.
     * The downloaded file for the removed download is not deleted.
     *
     * @param id id of download to be removed.
     * @param func callback used to report the successfully removed download.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch remove(int id, Func<Download> func, Func<Error> func2);

    /**
     * Remove a download managed by this instance of Fetch.
     * The downloaded file for the removed download is not deleted.
     *
     * @param id id of download to be removed.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch remove(int id);

    /**
     * Remove all downloads in the specified group managed by this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param id specified group id
     * @param func callback with results of removed downloads in the group that were removed.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeGroup(int id, Func<List<Download>> func, Func<Error> func2);

    /**
     * Remove all downloads in the specified group managed by this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param id specified group id
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeGroup(int id);

    /**
     * Remove all downloads managed by this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param func callback reporting all downloads that were removed.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAll(Func<List<Download>> func, Func<Error> func2);

    /**
     * Remove all downloads managed by this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAll();

    /**
     * Remove all downloads with the specified status in this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param status status
     * @param func callback returning a list of downloads that were removed.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAllWithStatus(Status status, Func<List<Download>> func, Func<Error> func2);

    /**
     * Remove all downloads with the specified status in this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param status status
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAllWithStatus(Status status);

    /**
     * Remove all downloads with the specified group and status in this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param id id
     * @param statuses statuses
     * @param func callback returning a list of downloads that were removed.
     * @param func2 Callback that is called when attempting to remove downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAllInGroupWithStatus(int id, List<Status> statuses, Func<List<Download>> func, Func<Error> func2);

    /**
     * Remove all downloads with the specified group and status in this instance of Fetch.
     * The downloaded files for removed downloads are not deleted.
     *
     * @param id id
     * @param statuses statuses
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeAllInGroupWithStatus(int id, List<Status> statuses);

    /**
     * Delete a list of downloads managed by this instance of Fetch.
     * The downloaded file is deleted.
     *
     * @param ids ids of downloads to be deleted.
     * @param func callback that returns the downloads that were deleted.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch delete(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Delete a list of downloads managed by this instance of Fetch.
     * The downloaded file is deleted.
     *
     * @param ids ids of downloads to be deleted.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch delete(List<Integer> ids);

    /**
     * Delete a download managed by this instance of Fetch.
     * The downloaded file is deleted.
     *
     * @param id id of download to be deleted.
     * @param func callback that returns the successfully deleted download.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch delete(int id, Func<Download> func, Func<Error> func2);

    /**
     * Delete a download managed by this instance of Fetch.
     * The downloaded file is deleted.
     *
     * @param id id of download to be deleted.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch delete(int id);

    /**
     * Deletes all downloads in the specified group managed by this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param id specified group id
     * @param func callback that returns the list of downloads that were deleted in the specified group.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteGroup(int id, Func<List<Download>> func, Func<Error> func2);

    /**
     * Deletes all downloads in the specified group managed by this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param id specified group id
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteGroup(int id);

    /**
     * Deletes all downloads managed by this instance of Fetch.
     * The downloaded files are deleted.
     *
     * @param func callback returns the result of all deleted downloads.
     * @param func2 callback returns the result of all deleted downloads.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAll(Func<List<Download>> func, Func<Error> func2);

    /**
     * Deletes all downloads managed by this instance of Fetch.
     * The downloaded files are deleted.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAll();

    /**
     * Deletes all downloads with the specified status in this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param status status
     * @param func callback returns all deleted downloads with a specified status.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAllWithStatus(Status status, Func<List<Download>> func, Func<Error> func2);

    /**
     * Deletes all downloads with the specified status in this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param status status
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAllWithStatus(Status status);

    /**
     * Deletes all downloads with the specified group and status in this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param id id
     * @param statuses statuses
     * @param func callback returns all deleted downloads with a specified status.
     * @param func2 callback returns all deleted downloads with a specified status.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAllInGroupWithStatus(int id, List<Status> statuses, Func<List<Download>> func, Func<Error> func2);

    /**
     * Deletes all downloads with the specified group and status in this instance of Fetch.
     * The downloaded files are also deleted.
     *
     * @param id id
     * @param statuses statuses
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch deleteAllInGroupWithStatus(int id, List<Status> statuses);

    /**
     * Cancel a list of non completed downloads managed by this instance of Fetch.
     * The downloaded file for the cancelled download is not deleted.
     *
     * @param ids ids of downloads to be cancelled.
     * @param func callback used to return the results of the cancelled downloads.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancel(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Cancel a list of non completed downloads managed by this instance of Fetch.
     * The downloaded file for the cancelled download is not deleted.
     *
     * @param ids ids of downloads to be cancelled.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancel(List<Integer> ids);

    /**
     * Cancel a non completed download managed by this instance of Fetch.
     * The downloaded file for the cancelled download is not deleted.
     *
     * @param id id of downloads to be cancelled.
     * @param func callback used to return the successful cancelled download.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancel(int id, Func<Download> func, Func<Error> func2);

    /**
     * Cancel a non completed download managed by this instance of Fetch.
     * The downloaded file for the cancelled download is not deleted.
     *
     * @param id id of downloads to be cancelled.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancel(int id);

    /**
     * Cancels all non completed downloads in the specified group managed by this instance of Fetch.
     * The downloaded files for cancelled downloads are not deleted.
     *
     * @param id specified group id
     * @param func callback that returns the list of cancelled downloads.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancelGroup(int id, Func<List<Download>> func, Func<Error> func2);

    /**
     * Cancels all non completed downloads in the specified group managed by this instance of Fetch.
     * The downloaded files for cancelled downloads are not deleted.
     *
     * @param id specified group id
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancelGroup(int id);

    /**
     * Cancels all non completed downloads managed by this instance of Fetch.
     * The downloaded files for cancelled downloads are not deleted.
     *
     * @param func callback that returns the list of cancelled downloads.
     * @param func2 Callback that is called when attempting to delete downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancelAll(Func<List<Download>> func, Func<Error> func2);

    /**
     * Cancels all non completed downloads managed by this instance of Fetch.
     * The downloaded files for cancelled downloads are not deleted.
     *
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch cancelAll();

    /**
     * Retries to download a list of failed or cancelled downloads.
     *
     * @param ids ids of the failed or cancelled downloads.
     * @param func callback that returns the list of downloads that were successfully queued.
     * @param func2 Callback that is called when attempting to retry downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch retry(List<Integer> ids, Func<List<Download>> func, Func<Error> func2);

    /**
     * Retries to download a failed or cancelled download.
     *
     * @param ids ids of the failed or cancelled downloads.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch retry(List<Integer> ids);

    /**
     * Retries to download a failed or cancelled download.
     *
     * @param id id of the failed or cancelled downloads.
     * @param func callback that returns the successful queued download.
     * @param func2 Callback that is called when attempting to retry downloads fail. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch retry(int id, Func<Download> func, Func<Error> func2);

    /**
     * Retries to download a failed or cancelled download.
     *
     * @param id id of the failed or cancelled downloads.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch retry(int id);

    /**
     * Updates an existing request.
     *
     * @param requestId Id of existing request/download
     * @param updatedRequest Request object
     * @param notifyListeners If the request is successfully updated notify attached Fetch listeners of the download status. Default true
     * @param func Successful callback that the download will be returned on.
     * @param func2 Failed callback that the error will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     * @see com.tonyodev.fetch2.Request for more details.
     */
    Fetch updateRequest(int requestId,
                        Request updatedRequest,
                        boolean notifyListeners,
                        Func<Download> func,
                        Func<Error> func2);

    /**
     * default impl
     *
     * @param requestId requestId
     * @param updatedRequest updatedRequest
     * @param func func
     * @param func2 func2
     * @return Fetch
     */
    default Fetch updateRequest(int requestId,
                                Request updatedRequest,
                                Func<Download> func,
                                Func<Error> func2) {
        return updateRequest(requestId, updatedRequest, false, func, func2);
    }

    /**
     * Replaces the existing extras object associated with an existing download/request with the newly passed in extras object.
     *
     * @param id Id of existing request/download
     * @param extras new extras object
     * @param func Successful callback that the download will be returned on.
     * @param func2 Failed callback that the error will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch replaceExtras(int id, Extras extras, Func<Download> func, Func<Error> func2);

    /**
     * Resets the autoRetryAttempts value for a download back to 0.
     *
     * @param downloadId Id of existing request/download
     * @param retryDownload Retry the download if its status is Status.ERROR. True by default.
     * @param func callback that returns the download if it exists.
     * @param func2 callback that returns the error if on occurred.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch resetAutoRetryAttempts(int downloadId, boolean retryDownload, Func2<Download> func, Func<Error> func2);

    /**
     * 默认可重试
     *
     * @param downloadId downloadId
     * @param func func
     * @param func2 func2
     * @return Fetch
     */
    public default Fetch resetAutoRetryAttempts(int downloadId, Func2<Download> func, Func<Error> func2) {
        resetAutoRetryAttempts(downloadId, true, func, func2);
        return this;
    }

    /**
     * Renames the file for a completed download. The StorageResolver attached to this fetch instance will rename the file.
     * So it is okay to parse uri strings for the newFileName.
     *
     * @param id Id of existing request/download
     * @param newFileName the new file name.
     * @param func Successful callback that the download will be returned on.
     * @param func2 Failed callback that the error will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch renameCompletedDownloadFile(int id, String newFileName, Func<Download> func, Func<Error> func2);

    /**
     * Gets all downloads managed by this instance of Fetch.
     *
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloads(Func<List<Download>> func);

    /**
     * Gets the downloads which match an id in the list. Only successful matches will be returned.
     *
     * @param idList Id list to perform id query against.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloads(List<Integer> idList, Func<List<Download>> func);

    /**
     * Gets the download which has the specified id. If the download
     * does not exist null will be returned.
     *
     * @param id Download id
     * @param func Callback that the results will be returned on. Result maybe null.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownload(int id, Func2<Download> func);

    /**
     * Gets all downloads in the specified group.
     *
     * @param groupId group id to query.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloadsInGroup(int groupId, Func<List<Download>> func);

    /**
     * Gets all downloads with a specific status.
     *
     * @param status Status to query.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     * @see com.tonyodev.fetch2.Status
     */
    Fetch getDownloadsWithStatus(Status status, Func<List<Download>> func);

    /**
     * Gets all downloads with a specific status.
     *
     * @param statuses Statuses to query.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     * @see com.tonyodev.fetch2.Status
     */
    Fetch getDownloadsWithStatus(List<Status> statuses, Func<List<Download>> func);

    /**
     * Gets all downloads containing the identifier.
     *
     * @param identifier identifier.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloadsByRequestIdentifier(Long identifier, Func<List<Download>> func);

    /**
     * Gets all downloads in a specific group with a specific status.
     *
     * @param groupId group id to query.
     * @param statuses Statuses to query.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     * @see com.tonyodev.fetch2.Status
     */
    Fetch getDownloadsInGroupWithStatus(int groupId, List<Status> statuses, Func<List<Download>> func);

    /**
     * Gets all downloads containing the tag.
     *
     * @param tag tag.
     * @param func Callback that the results will be returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloadsByTag(String tag, Func<List<Download>> func);

    /**
     * Gets the FetchGroup by id. Even if the database does not contain downloads with this group id
     * a FetchGroup will be returned. It will contain no downloads however. When a download with this
     * group id is added. The downloads field on this object will be update and attached FetchObservers will be notified.
     *
     * @param group the group id
     * @param func callback that the results will be returned on.
     * @return Instance.
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getFetchGroup(int group, Func<FetchGroup> func);

    /**
     * Gets a list of the ids of all groups managed my this fetch namespace.
     *
     * @param func callback that the results will be returned on.
     * @return Instance.
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getAllGroupIds(Func<List<Integer>> func);

    /**
     * Attaches a FetchListener to this instance of Fetch.
     *
     * @param listener Fetch Listener
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    public default Fetch addListener(FetchListener listener) {
        return addListener(listener, false);
    }

    /**
     * Attaches a FetchListener to this instance of Fetch.
     *
     * @param listener Fetch Listener
     * @param notify Allows Fetch to notify the newly attached listener instantly of the download status
     * of all downloads managed by the namespace. Default is false.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch addListener(FetchListener listener, boolean notify);

    /**
     * Attaches a FetchListener to this instance of Fetch.
     *
     * @param listener Fetch Listener
     * @param notify Allows Fetch to notify the newly attached listener instantly of the download status
     * of all downloads managed by the namespace. Default is false.
     * @param autoStart Allows Fetch to start processing requests if it is not already doing so.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch addListener(FetchListener listener, boolean notify, boolean autoStart);

    /**
     * Detaches a FetchListener from this instance of Fetch.
     *
     * @param listener Fetch Listener
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeListener(FetchListener listener);

    /**
     * Adds a completed download to Fetch for management. If Fetch is already managing another download with the same file as this completed download's
     * file, Fetch will replace the already managed download with this completed download.
     *
     * @param completedDownload Completed Download
     * @param alertListeners boolean indicating whether to alert all listeners attached to this fetch's namespace of the downloads completed status.
     * @param func Callback that the added download will be returned on.
     * @param func2 Callback that is called when adding the completed download fails. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch addCompletedDownload(CompletedDownload completedDownload, boolean alertListeners, Func<Download> func, Func<Error> func2);

    /**
     * Adds a list of completed downloads to Fetch for management. If Fetch is already managing another download with the same file as this completed download's
     * file, Fetch will replace the already managed download with this completed download.
     *
     * @param completedDownloads Completed Downloads list
     * @param alertListeners boolean indicating whether to alert all listeners attached to this fetch's namespace of the downloads completed status.
     * @param func Callback that the added downloads list will be returned on.
     * @param func2 Callback that is called when adding the completed downloads fails. An error is returned.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch addCompletedDownloads(List<CompletedDownload> completedDownloads, boolean alertListeners, Func<List<Download>> func, Func<Error> func2);

    /**
     * Gets the list of download blocks belonging to a download. List may be empty if
     * blocks could not be found for the download id or download has never been processed.
     *
     * @param downloadId Download ID
     * @param func Callback the results will be returned on
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getDownloadBlocks(int downloadId, Func<List<DownloadBlock>> func);

    /**
     * Gets the content Length for a request. If the request or contentLength cannot be found in
     * the Fetch database(meaning Fetch never processed the request and started downloading it) -1 is returned.
     * However, setting fromServer to true will create a new connection to the server to get the connectLength
     * if Fetch does not already contain the data in the database for the request.
     *
     * @param request Request. Can be a managed or un-managed request. The request is not stored in
     * the fetch database.
     * @param fromServer If true, fetch will attempt to get the ContentLength
     * from the server directly by making a network request. Otherwise no action is taken.
     * @param func callback result will be returned on. If the result is -1. This indicates that
     * Fetch was not able to get the ContentLength.
     * @param func2 where the error will be returned if one occurs. This indicates that
     * Fetch was not able to get the ContentLength.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getContentLengthForRequest(Request request, boolean fromServer, Func<Long> func, Func<Error> func2);

    /**
     * Gets the content Length for each request in the passed in list. If the request or contentLength cannot be found in
     * the Fetch database(meaning Fetch never processed the request and started downloading it) -1 is returned.
     * However, setting fromServer to true will create a new connection to the server to get the connectLength
     * if Fetch does not already contain the data in the database for the request.
     *
     * @param requests Request list. Can be a managed or un-managed list of requests. The requests are not stored in
     * the fetch database.
     * @param fromServer If true, fetch will attempt to get the ContentLength
     * from the server directly by making a network request. Otherwise no action is taken.
     * @param func callback which returns a list of all the success request pairs with their content length.
     * @param func2 callback used to return a list of all the request that error when trying to get their content length.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getContentLengthForRequests(List<Request> requests, boolean fromServer, Func<List<Pair<Request, Long>>> func, Func<List<Pair<Request, Error>>> func2);

    /**
     * Gets the Server Response for the url and associated headers.
     *
     * @param url the url. Cannot be null.
     * @param headers the request headers for the url. Can be null.
     * @param func the callback the server response is returned on. Cannot be null.
     * @param func2 the callback that is executed with an error occurs. Can be null.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getServerResponse(String url, Map<String, String> headers, Func<Downloader.Response> func, Func<Error> func2);

    /**
     * Gets the full File Resource Catalog of a Fetch File Server.
     *
     * @param request Request. Can be a managed or un-managed request. The request is not stored in
     * the fetch database.
     * @param func callback the result is returned on.
     * @param func2 callback the error is returned on.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch getFetchFileServerCatalog(Request request, Func<List<FileResource>> func, Func<Error> func2);

    /**
     * Enable or disable logging.
     *
     * @param enabled Enable or disable logging.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch enableLogging(Boolean enabled);

    /**
     * Overrides each downloads specific network type preference and uses a
     * global network type preference instead.
     * Use com.tonyodev.fetch2.NetworkType.GLOBAL_OFF to disable the global network preference.
     * The default value is com.tonyodev.fetch2.NetworkType.GLOBAL_OFF
     *
     * @param networkType The global network type.
     * @return Instance
     * @throws FetchException if this instance of Fetch has been closed.
     * @see com.tonyodev.fetch2.NetworkType
     */
    Fetch setGlobalNetworkType(NetworkType networkType);

    /**
     * Sets the number of parallel downloads Fetch should perform at any given time.
     * Default value is 1. This method can only accept values greater than 0. Setting
     * concurrent limit to zero prevents the instance of Fetch to pull and download request
     * from the waiting queue but allows the instance of Fetch to act on and observe changes to
     * requests/downloads.
     *
     * @param downloadConcurrentLimit Number of parallel downloads.
     * @return Instance
     * @throws FetchException if the passed in download concurrent limit is less than 0 or
     * Fetch instance has been closed.
     */
    Fetch setDownloadConcurrentLimit(int downloadConcurrentLimit);

    /**
     * Releases held resources and the namespace used by this Fetch instance.
     * Once closed this instance cannot be reused but the namespace can be reused
     * by a new instance of Fetch.
     */
    void close();

    /**
     * Gets the set of FetchListeners attached to this instance.
     *
     * @return set of attached FetchListeners.
     */
    Set<FetchListener> getListenerSet();

    /**
     * Blocks the current thread(Not Ui Thread) to waiting on one of the two conditions.
     * Condition One: Waits until Fetch has downloaded all downloading and pending downloads.
     * Condition Two: Waits until the allow time expires
     * if Fetch has not completed or attempted to download queued downloads.
     * This method returns when one of the conditions if violated.
     * Note: Calling this method on the UIThread is strongly discouraged and an exception is thrown.
     *
     * @param allowTimeInMilliseconds the allowed time in milliseconds. If zero the wait is indefinite.
     * @throws FetchException if calling on the main thread
     */
    void awaitFinishOrTimeout(Long allowTimeInMilliseconds);

    /**
     * Blocks the current thread(Not Ui Thread) to waiting on the current conditions.
     * Condition One: Waits until Fetch has downloaded all downloading and pending downloads.
     * This method returns when one of the conditions if violated.
     * Note: Calling this method on the UIThread is strongly discouraged and an exception is thrown.
     *
     * @throws FetchException if calling on the main thread
     */
    void awaitFinish();

    /**
     * Attaches a FetchObserver to listen for changes on a download managed by this Fetch namespace.
     * FetchObservers are held with a weak reference. Note: If fetch does not manage a download with
     * the passed in id, the FetchObserver will not be notified. Only when a download with the specified
     * id is managed by Fetch will the observer be called.
     *
     * @param downloadId the download Id
     * @param fetchObservers the fetch observers
     * @return instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch attachFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers);

    /**
     * Removes a FetchObserver attached to this Fetch namespace for a download.
     *
     * @param downloadId the download Id
     * @param fetchObservers the fetch observers
     * @return instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers);

    /**
     * Indicates if this fetch namespace has active(Queued or Downloading) downloads. You can use this value to
     * keep a background service ongoing until the callback public Function returns false.
     *
     * @param includeAddedDownloads To include downloads with a status of Added. Added downloads are not considered active by default.
     * @param func the callback public Function
     * @return instance
     * @throws FetchException if accessed on ui thread
     */
    Fetch hasActiveDownloads(boolean includeAddedDownloads, Func<Boolean> func);

    /**
     * Subscribe a FetchObserver that indicates if this fetch namespace has active(Queued or Downloading) downloads. You can use this value to
     * keep a background service ongoing until the value returned is false.
     *
     * @param includeAddedDownloads To include downloads with a status of Added. Added downloads are not considered active by default.
     * @param fetchObservers the fetch observer
     * @return instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch addActiveDownloadsObserver(boolean includeAddedDownloads, FetchObserver<Boolean> fetchObservers);

    /**
     * Removes a subscribed FetchObserver that is listening for active downloads.
     *
     * @param fetchObservers the fetch observer to remove.
     * @return instance
     * @throws FetchException if this instance of Fetch has been closed.
     */
    Fetch removeActiveDownloadsObserver(FetchObserver<Boolean> fetchObservers);

    /**
     * Fetch implementation class. Use this Singleton to get instances of Fetch.
     *
     * @since 2021-06-17
     */
    public static final class Impl {

        private static final Object lock = new Object();
        private static volatile Fetch defaultFetchInstance;
        private static volatile FetchConfiguration defaultFetchConfiguration;

        /**
         * Sets the default Configuration settings on the default Fetch instance.
         *
         * @param fetchConfiguration custom Fetch Configuration
         */
        public static void setDefaultInstanceConfiguration(FetchConfiguration fetchConfiguration) {
            synchronized (lock) {
                defaultFetchConfiguration = fetchConfiguration;
            }
        }

        /**
         * Get the default Fetch Configuration set with setDefaultInstanceConfiguration(fetchConfiguration: FetchConfiguration)
         * or setDefaultInstanceConfiguration(context: Context)
         *
         * @return default FetchConfiguration
         */
        public FetchConfiguration getDefaultFetchConfiguration() {
            synchronized (lock) {
                return defaultFetchConfiguration;
            }
        }

        /**
         * getDefaultInstance
         * @return Get default Fetch instance
         * @throws FetchException if default FetchConfiguration is not set.
         */
        public static Fetch getDefaultInstance() {
            synchronized (lock) {
                FetchConfiguration fetchConfiguration;
                if (defaultFetchConfiguration == null) {
                    throw new FetchException(FetchErrorStrings.GLOBAL_FETCH_CONFIGURATION_NOT_SET);
                }
                fetchConfiguration = defaultFetchConfiguration;
                Fetch defaultFetch = defaultFetchInstance;
                if (defaultFetch == null || defaultFetch.isClosed()) {
                    FetchImpl newDefaultFetch = FetchImpl.newInstance(FetchModulesBuilder.getInstance().buildModulesFromPrefs(fetchConfiguration));
                    defaultFetchInstance = newDefaultFetch;
                    return newDefaultFetch;
                } else {
                    return defaultFetch;
                }
            }
        }

        /**
         * Creates a custom Instance of Fetch with the given configuration and namespace.
         *
         * @param fetchConfiguration custom Fetch Configuration
         * @return custom Fetch instance
         */
        public static Fetch getInstance(FetchConfiguration fetchConfiguration) {
            return FetchImpl.newInstance(FetchModulesBuilder.getInstance().buildModulesFromPrefs(fetchConfiguration));
        }
    }
}
